
//-------------------------------------------------------------------------------------
//StateObject.h
//
//Desc:
//Define render state objects. Low level renderer will map these value to 
//platform specific states.It's better to create a fixed sets of state object
//at startup time, change whole state object rather than individual value. 
//
//Author:Clayman
//-------

#pragma once
#include "../../MathLib/DXMath3.03fix/FlameMath.h"
#include <memory> 
#include <iostream>
#include <stdint.h>


namespace FlameEngine
{
	enum FillMode :int8_t
	{		
		fm_wireframe,
		fm_solid,
	};

	enum CullMode :int8_t
	{
		cm_none,
		cm_front,
		cm_back,
	};

	enum CompareFunction :int8_t
	{
		cf_never,
		cf_less,
		cf_equal,
		cf_lessEqual,
		cf_notEqual,
		cf_greater,		
		cf_greaterEqual,
		cf_always,
	};

	enum StencilOperation :int8_t
	{
		so_keep,
		so_zero,
		so_replace,
		so_incrementSat,
		so_decrementSat,
		so_invert,
		so_increment,
		so_decrement,
	};

	enum DepthWriteMask :int8_t
	{
		dwm_maskZero,
		dwm_maskAll,
	};

	enum Blend :int8_t
	{
		bld_zero,
		bld_one,
		bld_sourceColor,
		bld_invSourceColor,
		bld_sourceAlpha,
		bld_invSourceAlpha,
		bld_destAlpha,
		bld_invDestAlpha,
		bld_destColor,
		bld_sourceAlphaSat,
		bld_blendFactor,
		bld_invBlendFactor,
		bld_source1Color,
		bld_invSource1Color,
		bld_source1Alpha,
		bld_invSource1Alpha,
	};

	enum BlendOperation :int8_t
	{
		bo_add,
		bo_subtract,
		bo_revSubtract,
		bo_min,
		bo_max,
	};

	enum RTWriteMaskFlag :uint8_t
	{
		wmf_redEnable = 1,
		wmf_greenEnable = 2,
		wmf_blueEnable = 4,
		wmf_alphaEnable = 8,
		wmf_allEnable = 0xf,
	};

	enum TextureFilter :int8_t
	{
		tf_minMagMipPoint,
		tf_minMagPointMipLinear,
		tf_minPointMagLinearMipPoint,
		tf_minPointMagMipLinear,
		tf_minLinearMagMipPoint,
		tf_minLinearMagPointMipLinear,
		tf_minMagLinearMipPoint,
		tf_minMagMipLinear,
		tf_anisotropic,

		tf_comparisonMinMagMipPoint,
		tf_comparisonMinMagPointMipLinear,
		tf_comparisonMinPointMagLinearMipPoint,
		tf_comparisonMinPointMagMipLinear,
		tf_comparisonMinLinearMagMipPoint,
		tf_comparisonMinLinearMagPointMipLinear,
		tf_comparisonMinMagLinearMipPoint,
		tf_comparisonMinMagMipLinear,
		tf_comparisonAnisotropic,

		tf_text1Bit,
	};

	enum TextureAddressMode :int8_t
	{
		tam_wrap,
		tam_mirror,
		tam_clamp,
		tam_border,
		tam_mirrorOnce,
	};

	class RasterizerState;
	class DepthStencilState;
	class BlendState;
	class BlendStateDesc;
	class RasterizerStateDesc;
	class DepthStencilStateDesc;
	class SamplerState;
	class SamplerDesc;


	//----------------rasterizer state--------------------
	class RasterizerStateDesc final
	{
	public:
		int32_t depthBias;
		float depthBiasClamp;
		float slopeScaledDepthBias;
		FillMode fillMode;
		CullMode cullMode;
		bool counterClockwiseFrontFace;
		bool depthClipEnable;
		bool scissorEnable;
		bool multisampleEnable;
		bool antialiasedLineEnable;
		
		RasterizerStateDesc():depthBias(0),depthBiasClamp(0),slopeScaledDepthBias(0),
			counterClockwiseFrontFace(false),depthClipEnable(true),scissorEnable(false),
			multisampleEnable(false),antialiasedLineEnable(false),fillMode(fm_solid),cullMode(cm_back)
		{}

		//Calc a hash code base on current state value, hash code may change after
		//modify state value,don't use it as ID.
		uint32_t CalcHashCode() const;
	};	

	class RasterizerState
	{
	public:
		RasterizerState();
		explicit RasterizerState(const RasterizerStateDesc& desc);
		
		inline FillMode GetFillMode() const;
		inline CullMode GetCullMode() const;
		inline int32_t GetDepthBias() const;
		inline float GetDepthBiasClamp() const;
		inline float GetSlopeScaledDepthBias() const;
		inline bool IsDepthClipEnable() const;
		inline bool IsScissorEnable() const	;
		inline bool IsMultisampleEnable() const;
		inline bool IsAntialiasedLineEnable() const;
		inline bool IsCounterClockwiseFrontFace() const;

		void SetFillMode(FillMode _value);
		void SetCullMode(CullMode _value);
		void SetDepthBias(int32_t _value);
		void SetDepthBiasClamp(float _value);
		void SetSlopeScaledDepthBias(float _value);
		void SetDepthClipEnable(bool _value);
		void SetScissorEnable(bool _value);
		void SetMultisampleEnable(bool _value);
		void SetAntialiasedLineEnalbe(bool _value);
		void SetCounterClockwiseFrontFace(bool _value);
		
		const RasterizerStateDesc& GetStateDesc() const {return m_stateDesc;}
		inline uint32_t GetHashCode() const;
		inline bool IsDirty() const;
		
	private:
		RasterizerStateDesc m_stateDesc;
		mutable uint32_t m_hashCode;
		mutable bool m_isDirty;

		RasterizerState(const RasterizerState&);
		RasterizerState& operator=(const RasterizerState);

		void CalcHashCode() const;
	};

	//---------------depth stencil state-------------------
	class DepthStencilStateDesc final
	{
	public:
		bool depthEnable;
		bool stencilEnable;
		uint8_t stencilReadMask;
		uint8_t stencilWriteMask;
		DepthWriteMask depthWriteMask;
		CompareFunction depthFunciton;

		CompareFunction ffStencilFunction;
		CompareFunction bfStencilFunction;
		StencilOperation ffStencilFailOp;
		StencilOperation ffStencilDepthFailOp;
		StencilOperation ffStencilPassOp;
		StencilOperation bfStencilFailOp;
		StencilOperation bfStencilDepthFailOp;
		StencilOperation bfStencilPassOp;

		DepthStencilStateDesc():depthEnable(true),stencilEnable(false),stencilReadMask(0xff),stencilWriteMask(0xff),
			depthWriteMask(dwm_maskAll),depthFunciton(cf_less),ffStencilFunction(cf_less),bfStencilFunction(cf_always),
			ffStencilFailOp(so_keep),ffStencilDepthFailOp(so_keep),ffStencilPassOp(so_keep),
			bfStencilFailOp(so_keep),bfStencilDepthFailOp(so_keep),bfStencilPassOp(so_keep)
		{}

		uint32_t CalcHashCode() const;
	};

	class DepthStencilState
	{
	public:
		DepthStencilState();
		explicit DepthStencilState(const DepthStencilStateDesc& _desc);

		inline bool IsDepthEnable() const;
		inline DepthWriteMask GetDepthWriteMask() const;
		inline CompareFunction GetDepthFunction() const;
		inline bool IsStencilEnable() const;
		inline uint8_t GetStencilReadMask() const;
		inline uint8_t GetStencilWriteMask() const;
		inline StencilOperation GetFFStencilFailOperation() const;
		inline StencilOperation GetFFStencilDepthFailOperation() const;
		inline StencilOperation GetFFStencilPassOperation() const;
		inline CompareFunction GetFFStencilFunc() const;
		inline StencilOperation GetBFStencilFailOperation() const;
		inline StencilOperation GetBFStencilDepthFailOperation() const;
		inline StencilOperation GetBFStencilPassOperation() const;
		inline CompareFunction GetBFStencilFunc() const;

		void SetDepthEnable(bool enable);
		void SetDepthWriteMask(DepthWriteMask mask);
		void SetDepthFunction(CompareFunction func);
		void SetStencilEnable(bool enable);
		void SetStencilReadMask(uint8_t mask);
		void SetStencilWriteMask(uint8_t mask);
		void SetFFStencilFailOperation(StencilOperation op);
		void SetFFStencilDepthFailOperation(StencilOperation op);
		void SetFFStencilPassOperation(StencilOperation op);
		void SetFFStencilFunc(CompareFunction func);
		void SetBFStencilFailOperation(StencilOperation op);
		void SetBFStencilDepthFailOperation(StencilOperation op);
		void SetBFStencilPassOperation(StencilOperation op);
		void SetBFStencilFunc(CompareFunction func);
		
		const DepthStencilStateDesc& GetStateDesc()const {return m_stateDesc;}

		inline bool IsDirty() const {return m_isDirty;}
		uint32_t GetHashCode() const;

	private:
		DepthStencilStateDesc m_stateDesc;
		mutable bool m_isDirty;
		mutable uint32_t m_hashCode;
		mutable int32_t m_userData;

		DepthStencilState(const DepthStencilState&);
		DepthStencilState& operator=(const DepthStencilState);
	};

	//---------------blend state--------------------------
	struct RTBlendDesc final
	{
		RTWriteMaskFlag writeMask :8;
		Blend srcBlend	:4;
		Blend destBlend :4;
		Blend srcBlendAlpha :4;
		Blend destBlendAlpha :4;
		BlendOperation blendOp:3;
		bool blendEnable:1;
		BlendOperation blendOpAlpha:3;

		RTBlendDesc::RTBlendDesc()
			:writeMask(wmf_allEnable),srcBlend(bld_one),destBlend(bld_zero),srcBlendAlpha(bld_one),
			destBlendAlpha(bld_zero),blendOp(bo_add),blendOpAlpha(bo_add),blendEnable(false)
		{
		}
	};
	
	class BlendStateDesc
	{
	public: 
		bool alphaToCoverageEnable;
		bool independentBlendEnable;
		static const uint8_t maxRTCount = 8;
		RTBlendDesc rtBlendState[maxRTCount];

		BlendStateDesc():alphaToCoverageEnable(false),independentBlendEnable(false)
		{
			RTBlendDesc defaultState;
			for(int i=0;i<maxRTCount;i++)
				rtBlendState[i] = defaultState;
		}

		uint32_t CalcHashCode() const;
	};

	class BlendState 
	{
	public:
		BlendState();
		explicit BlendState(const BlendStateDesc& desc);

		inline bool IsAlphaToCoverageEnable() const;
		inline bool IsIndependentBlendEnable() const;
		inline bool IsBlendEnable(uint8_t renderTargetIndex = 0) const;
		inline Blend GetSourceBlend(uint8_t renderTargetIndex = 0) const;
		inline Blend GetDestBlend(uint8_t renderTargetIndex = 0) const;
		inline BlendOperation GetBlendOperation(uint8_t renderTargetIndex = 0) const;
		inline Blend GetSourceBlendAlpha(uint8_t renderTargetIndex = 0) const;
		inline Blend GetDestBlendAlpha(uint8_t renderTargetIndex = 0) const;
		inline BlendOperation GetAlphaOperation(uint8_t renderTargetIndex = 0) const;
		inline RTWriteMaskFlag GetRenderTargetWriteMask(uint8_t renderTargetIndex = 0) const;

		void SetAlphaToCoverageEnable(bool enable);
		void SetIndependentBlendEnable(bool enbale);
		void SetBlendEnable(bool enable,uint8_t renderTargetIndex = 0);
		void SetSourceBlend(Blend blend,uint8_t renderTargetIndex = 0);
		void SetDestBlend(Blend blend,uint8_t renderTargetIndex = 0);
		void SetBlendOperation(BlendOperation blendOp,uint8_t renderTargetIndex = 0);
		void SetSourceBlendAlpha(Blend blend,uint8_t renderTargetIndex = 0);
		void SetDestBlendAlpha(Blend blend,uint8_t renderTargetIndex = 0);
		void SetAlphaOperation(BlendOperation blendOp,uint8_t renderTargetIndex = 0);
		void SetRenderTargetWriteMask(RTWriteMaskFlag mask,uint8_t renderTargetIndex = 0);

		inline bool	IsDirty() const{return m_isDirty;}
		uint32_t GetHashCode() const;
		const BlendStateDesc& GetStateDesc()const {return m_stateDesc;}
		
		
	private:
		BlendStateDesc m_stateDesc;
		mutable bool m_isDirty;
		mutable uint32_t m_hashCode;

		BlendState(const BlendState&);
		BlendState& operator=(const BlendState);
	};

	//-------------sampler state---------------------------
	class SamplerDesc final
	{
	public:
		uint8_t MipLodBias;
		uint8_t MaxAnisotropy;
		uint8_t MinLod;
		uint8_t MaxLod;
		TextureFilter Filter;
		TextureAddressMode AddressModeU;
		TextureAddressMode AddressModeV;
		TextureAddressMode AddressModeW;
		CompareFunction CompareFunc;
		Float4 BorderColor;

		SamplerDesc():Filter(tf_minMagMipLinear),AddressModeU(tam_clamp),AddressModeV(tam_clamp),
			AddressModeW(tam_clamp),MipLodBias(0),MaxAnisotropy(16),CompareFunc(cf_never),
			MinLod(0),MaxLod(255),BorderColor(0,0,0,0)
		{
		}

		uint32_t CalcHashCode() const;
	};

	class SamplerState final
	{
	public:
		SamplerState();
		explicit SamplerState(const SamplerDesc& desc);

		uint8_t GetMipLodBias() const;
		uint8_t GetMinLod() const;
		uint8_t GetMaxLod() const;
		uint8_t GetMaxAnisotropy() const;
		TextureFilter GetFilterType() const;
		CompareFunction GetComparisonFunc() const;
		TextureAddressMode GetAddressModeU() const;
		TextureAddressMode GetAddressModeV() const;
		TextureAddressMode GetAddressModeW() const;
		Float4 GetBorderColor() const;

		void SetFilter(TextureFilter filter);
		void SetAddressModeU(TextureAddressMode mode);
		void SetAddressModeV(TextureAddressMode mode);
		void SetAddressModeW(TextureAddressMode mode);
		void SetLodBias(uint8_t bias);
		void SetMinLod(uint8_t minLod);
		void SetMaxLod(uint8_t maxLod);
		void SetBorderColor(const Float4& color);
		void SetCompareFunction(CompareFunction func);

		inline bool IsDirty(){return m_isDirty;}
		uint32_t GetHashCode() const;
		const SamplerDesc& GetSamplerDesc() const {return m_stateDesc;}

	private:
		SamplerDesc m_stateDesc;
		mutable bool m_isDirty;
		mutable uint32_t m_hashCode;
	};


#pragma region InineFuncions
	//RasterizerState
	inline bool RasterizerState::IsDirty()const
	{
		return m_isDirty;
	}
	
	uint32_t RasterizerState::GetHashCode() const
	{
		if(IsDirty())
		{
			m_hashCode = m_stateDesc.CalcHashCode();
			m_isDirty = false;
		}
		return m_hashCode;
	}

	inline FillMode RasterizerState::GetFillMode() const
	{
		return m_stateDesc.fillMode;
	}

	inline CullMode RasterizerState::GetCullMode() const
	{
		return m_stateDesc.cullMode;
	}

	inline int32_t RasterizerState::GetDepthBias() const
	{
		return m_stateDesc.depthBias;
	}

	inline float RasterizerState::GetDepthBiasClamp() const
	{
		return m_stateDesc.depthBiasClamp;
	}

	inline float RasterizerState::GetSlopeScaledDepthBias() const
	{
		return m_stateDesc.slopeScaledDepthBias;
	}

	inline bool RasterizerState::IsCounterClockwiseFrontFace() const
	{
		return m_stateDesc.counterClockwiseFrontFace;
	}

	inline bool RasterizerState::IsScissorEnable() const
	{
		return m_stateDesc.scissorEnable;
	}

	inline bool RasterizerState::IsMultisampleEnable() const
	{
		return m_stateDesc.multisampleEnable;
	}

	inline bool RasterizerState::IsAntialiasedLineEnable() const 
	{
		return m_stateDesc.antialiasedLineEnable;
	}

	inline bool RasterizerState::IsDepthClipEnable() const 
	{
		return m_stateDesc.depthClipEnable;
	}


//---------------depth stencil state---------------------------
	inline bool DepthStencilState::IsDepthEnable() const
	{
		return m_stateDesc.depthEnable;
	}
	
	inline DepthWriteMask DepthStencilState::GetDepthWriteMask() const
	{
		return m_stateDesc.depthWriteMask;
	}

	inline CompareFunction DepthStencilState::GetDepthFunction() const
	{
		return m_stateDesc.depthFunciton;
	}

	inline bool DepthStencilState::IsStencilEnable() const
	{
		return m_stateDesc.stencilEnable;
	}

	inline uint8_t DepthStencilState::GetStencilReadMask() const
	{
		return m_stateDesc.stencilReadMask;
	}

	inline uint8_t DepthStencilState::GetStencilWriteMask() const
	{
		return m_stateDesc.stencilWriteMask;
	}

	inline StencilOperation DepthStencilState::GetFFStencilFailOperation() const
	{
		return m_stateDesc.ffStencilFailOp;
	}

	inline StencilOperation DepthStencilState::GetFFStencilDepthFailOperation() const
	{
		return m_stateDesc.ffStencilDepthFailOp;
	}

	inline StencilOperation DepthStencilState::GetFFStencilPassOperation() const
	{
		return m_stateDesc.ffStencilPassOp;
	}

	inline CompareFunction DepthStencilState::GetFFStencilFunc() const
	{
		return m_stateDesc.ffStencilFunction;
	}

	inline StencilOperation DepthStencilState::GetBFStencilFailOperation() const
	{
		return m_stateDesc.bfStencilFailOp;
	}

	inline StencilOperation DepthStencilState::GetBFStencilDepthFailOperation() const
	{
		return m_stateDesc.bfStencilDepthFailOp;
	}

	inline StencilOperation DepthStencilState::GetBFStencilPassOperation() const
	{
		return m_stateDesc.bfStencilPassOp;
	}

	inline CompareFunction DepthStencilState::GetBFStencilFunc() const
	{
		return m_stateDesc.bfStencilFunction;
	}

	inline uint32_t DepthStencilState::GetHashCode() const
	{
		if(IsDirty())
		{
			m_hashCode = m_stateDesc.CalcHashCode();
			m_isDirty = false;
		}
		return m_hashCode;
	}

//--------------------blend state----------------------------------------
	inline bool BlendState::IsAlphaToCoverageEnable()const
	{
		return m_stateDesc.alphaToCoverageEnable;
	}

	inline bool BlendState::IsIndependentBlendEnable()const
	{
		return m_stateDesc.independentBlendEnable;
	}

	inline bool BlendState::IsBlendEnable(uint8_t rtIndex)const
	{
		rtIndex = rtIndex % BlendStateDesc::maxRTCount;
		return m_stateDesc.rtBlendState[rtIndex].blendEnable;
	}

	inline Blend BlendState::GetSourceBlend(uint8_t rtIndex)const
	{
		rtIndex = rtIndex % BlendStateDesc::maxRTCount;
		return m_stateDesc.rtBlendState[rtIndex].srcBlend;
	}

	inline Blend BlendState::GetDestBlend(uint8_t rtIndex)const
	{
		rtIndex = rtIndex % BlendStateDesc::maxRTCount;
		return m_stateDesc.rtBlendState[rtIndex].destBlend;
	}

	inline Blend BlendState::GetSourceBlendAlpha(uint8_t rtIndex)const
	{
		rtIndex = rtIndex % BlendStateDesc::maxRTCount;
		return m_stateDesc.rtBlendState[rtIndex].srcBlendAlpha;
	}

	inline Blend BlendState::GetDestBlendAlpha(uint8_t rtIndex )const
	{
		rtIndex = rtIndex % BlendStateDesc::maxRTCount;
		return m_stateDesc.rtBlendState[rtIndex].destBlendAlpha;
	}

	inline BlendOperation BlendState::GetBlendOperation(uint8_t rtIndex )const
	{		
		rtIndex = rtIndex % BlendStateDesc::maxRTCount;
		return m_stateDesc.rtBlendState[rtIndex].blendOp;
	}

	inline BlendOperation BlendState::GetAlphaOperation(uint8_t rtIndex )const
	{
		rtIndex = rtIndex % BlendStateDesc::maxRTCount;
		return m_stateDesc.rtBlendState[rtIndex].blendOpAlpha;
	}

	inline RTWriteMaskFlag BlendState::GetRenderTargetWriteMask(uint8_t rtIndex )const
	{
		rtIndex = rtIndex % BlendStateDesc::maxRTCount;
		return m_stateDesc.rtBlendState[rtIndex].writeMask;
	}

	inline uint32_t BlendState::GetHashCode() const
	{
		if(IsDirty())
		{
			m_hashCode = m_stateDesc.CalcHashCode();
			m_isDirty = false;
		}
		return m_hashCode;
	}

//----------------sample state--------------------------------
	inline TextureFilter SamplerState::GetFilterType() const
	{
		return m_stateDesc.Filter;
	}

	inline TextureAddressMode SamplerState::GetAddressModeU() const
	{
		return m_stateDesc.AddressModeU;
	}
	
	inline TextureAddressMode SamplerState::GetAddressModeV() const
	{
		return m_stateDesc.AddressModeU;
	}

	inline TextureAddressMode SamplerState::GetAddressModeW() const
	{
		return m_stateDesc.AddressModeW;
	}

	inline uint8_t SamplerState::GetMipLodBias() const
	{
		return m_stateDesc.MipLodBias;
	}

	inline uint8_t SamplerState::GetMaxAnisotropy() const
	{
		return m_stateDesc.MaxAnisotropy;
	}

	inline CompareFunction SamplerState::GetComparisonFunc() const
	{
		return m_stateDesc.CompareFunc;
	}

	inline Float4 SamplerState::GetBorderColor() const
	{
		return m_stateDesc.BorderColor;
	}

	inline uint8_t SamplerState::GetMinLod() const
	{
		return m_stateDesc.MinLod;
	}

	inline uint8_t SamplerState::GetMaxLod() const
	{
		return m_stateDesc.MaxLod;
	}

	inline uint32_t SamplerState::GetHashCode() const
	{
		if(m_isDirty)
		{
			m_hashCode = m_stateDesc.CalcHashCode();
			m_isDirty = false;
		}
		return m_hashCode;
	}
#pragma endregion

}